package agent;
//
//import com.sun.tools.attach.VirtualMachine;无法通过编译
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

/**
 * https://www.cnblogs.com/silyvin/p/11260965.html
 * https://www.cnblogs.com/silyvin/p/11336727.html
 * Created by sunyuming on 19/7/28.
 */
public class MyTransformer implements ClassFileTransformer {

    private ClassPool classPool = new ClassPool(true);

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

        if(className.equals("agent/MyClient")) {
            System.out.println("类 " + className);
            try {
                CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
                for(CtBehavior ctBehavior : ctClass.getDeclaredBehaviors()) {
                    if(ctBehavior.getLongName().equals("agent.MyClient.print()")) {
                        System.out.println("开始处理方法 " + ctBehavior.getLongName());
                        ctBehavior.insertBefore("System.out.println(\"前置aop\");");
                        ctBehavior.insertAfter("System.out.println(\"后置aop\");");
                    }
                }
                return ctClass.toBytecode();

            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        } else
            return null;

    }

    public static void agentmain(String agentArgs, Instrumentation inst)
            throws ClassNotFoundException, UnmodifiableClassException,
            InterruptedException {
        System.out.println("Agent Main start " + agentArgs);
        inst.addTransformer(new MyTransformer(), true);
        inst.retransformClasses(MyClient.class);
    }

    public static void premain(String args, Instrumentation instrumentation) {
        System.out.println("开始premain " + args);
        ClassFileTransformer classFileTransformer = new MyTransformer();
        instrumentation.addTransformer(classFileTransformer);
    }

    /**
     * premain
     * MANIFEST.MF.premain
     * cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagentpre.jar
     * MANIFEST.MF
     * java -javaagent:/Users/sunyuming/Documents/tool/jars/myagentpre.jar=sun -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar
     */

    /**
     * agentmain
     * MANIFEST.MF
     * cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagented.jar
     * java -jar ~/Documents/tool/jars/myagented.jar
     * 另一个terminal
     * MANIFEST.MF.agentmain
     * jps
     * java -Djava.ext.dirs=${JAVA_HOME}/lib -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar 47929
     */

    public static void main(String [] args) throws Exception {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class vmClass = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
        Object vm = vmClass.getMethod("attach", String.class).invoke(null, args[0]);
        String path = MyTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        // main与agentmain也可以分开，体现在path上，我们这边合并
        vmClass.getMethod("loadAgent", String.class, String.class).invoke(vm, path, "");

// 无法通过编译，除非在pom中加入tool，即使如此，运行期也需要 -Djava.ext.dirs=${JAVA_HOME}/lib
//        VirtualMachine vm = VirtualMachine.attach(args[0]); //正在运行的java 程序 ps id
//        vm.loadAgent("/Users/sunyuming/work/MyTest/target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar");

    }
}
